#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<assert.h>
#include<string.h>
#include<ctype.h>
#include<errno.h>

#define NUM 1024
#define OPT_NUM 64

#define NONE_REDIR 0
#define INPUT_REDIR 1
#define OUTPUT_REDIR 2
#define APPEND_REDIR 3

//宏函数
#define trimSpace(start) do{\
            while(isspace(*start)) start++;\
        }while(0)


char lineCommand[NUM];
char *myargv[OPT_NUM];

int lastCode;
int lastSig;


int redirType = NONE_REDIR;
char* redirFile = NULL;

void commandCheck(char* commands)
{
    assert(commands);
    char* start = commands;
    char* end = commands + strlen(commands);
    while(start != end)
    {
        if(*start == '>')
        {
            *start = '\0';
            start++;
            if(*start == '>'){
                start++;
                redirType = APPEND_REDIR;
            }
            else
            {
                redirType = OUTPUT_REDIR;
            }
            trimSpace(start);
            redirFile = start;
            break;
        }
        else if(*start == '<')
        {
            *start = '\0';
            start++;
            trimSpace(start);
            redirType = INPUT_REDIR;
            redirFile = start;
            break;
        }
        else
        {
            start++;
        }
    }
}

int main(){

    while(1){
         
        redirType = NONE_REDIR;
        redirFile = NULL;

        //输出提示符
        printf("[用户名@主机名 当前路径]$");
        fflush(stdout);
    
        //读取用户输入
        char* s = fgets(lineCommand,sizeof(lineCommand)-1,stdin);
        assert(s != NULL);
        (void)s;
        //清除最后的\n
        lineCommand[strlen(lineCommand)-1] = 0;

        commandCheck(lineCommand);

        myargv[0] = strtok(lineCommand," ");

        int i = 1;

        if(myargv[0] != NULL && strcmp(myargv[0],"ls") == 0)
        {
            myargv[i++] = (char*)"--color=auto";
        }
        while(myargv[i++] = strtok(NULL," "));



        if(myargv[0] != NULL && strcmp(myargv[0],"cd") == 0)
        {
            if(myargv[1] != NULL)chdir(myargv[1]);
            continue;
        }

        //内建命令
        if(myargv[0] != NULL && strcmp(myargv[0],"echo") == 0)
        {
            if(myargv[1] != NULL && strcmp(myargv[1],"$?") == 0)
            {
                printf("%d,%d\n",lastCode,lastSig);
                lastCode = 0;
                lastSig = 0;
                continue;
            }
        }
    
#ifdef DEBUG
    for(int i = 0;myargv[i];i++){
        printf("myargv[%d] = %s\n",i,myargv[i]);
    }
#endif

        pid_t id = fork();
        assert(id != -1);
        if(id == 0){
            switch(redirType)
            {
                case NONE_REDIR:
                    break;
                case INPUT_REDIR:
                    {
                        
                        int fd = open(redirFile,O_RDONLY);
                        if(fd < 0)
                        {
                            perror("open");
                            exit(errno);
                        }
                        dup2(fd,0);
                        close(fd);
                    }
                    break;
                case OUTPUT_REDIR:
                case APPEND_REDIR:
                    {

                        int flags = O_WRONLY | O_CREAT;
                        if(redirType == OUTPUT_REDIR) flags |= O_TRUNC;
                        else flags |= O_APPEND;

                        umask(0);
                        int fd = open(redirFile,flags,0666);
                        if(fd < 0)
                        {
                            perror("open");
                            exit(errno);
                        }
                        dup2(fd,1);
                        close(fd);
                    }
                    break;
                default:
                    printf("error\n");
                    break;
            }

            execvp(myargv[0],myargv);
            exit(-1);
        }
        int status = 0;
        waitpid(id,&status,0);
        lastCode = (status>>8) & 0xFF;
        lastSig = status & 0x7F;
    }

    return 0;
}
